package ru.usu.gv.uicomponents.viewitems.edge;

import com.sun.j3d.utils.geometry.Sphere;

import ru.usu.gv.core.GraphCanvas;
import ru.usu.gv.core.GraphEngine;
import ru.usu.gv.styles.StyleFacade;
import ru.usu.gv.uicomponents.UIElementBase;
import ru.usu.gv.uicomponents.UIUtilities;
import ru.usu.gv.uicomponents.UIVertex;
import ru.usu.gv.uicomponents.viewitems.ViewItemBase;
import ru.usu.gv.utils.GeometryUtil;
import ru.usu.gv.utils.splines.SplineUtilities;

import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Geometry;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.LineStripArray;
import javax.media.j3d.Node;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Color3f;
import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 @author Sergey Pupyrev 
 18.05.2007
 */

public class SplineEdgeItem extends ViewItemBase implements IEdgeViewItem
{
	private UIVertex v1;
	private UIVertex v2;

	private BranchGroup branchGroup;
	private Shape3D shape;
	private Point3d[] coordinates;
	private Point3d[] controlPoints;

	public SplineEdgeItem(Appearance appearance, TransformGroup transformGroup, UIVertex v1, UIVertex v2)
	{
		super(transformGroup, appearance, appearance.getMaterial());

		this.v1 = v1;
		this.v2 = v2;

		branchGroup = new BranchGroup();
		branchGroup.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
		branchGroup.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
		branchGroup.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);

		shape = new Shape3D();
		shape.setAppearance(appearance);
		shape.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
		shape.setCapability(Shape3D.ALLOW_BOUNDS_WRITE);
		shape.setBounds(GraphCanvas.getInstance().getBoundingSphere());

		controlPoints = new Point3d[] {};

		branchGroup.addChild(shape);
		transformGroup.addChild(branchGroup);
	}

	public void makePickable(UIElementBase ge)
	{
		ge.makePickable(shape);
	}

	@Override
	public void setColor(Color color)
	{
		super.setColor(color);
		Color3f color3f = new Color3f(color);
		getAppearance().getColoringAttributes().setColor(color3f);
	}

	public Point3d[] getControlPoints()
	{
		return controlPoints;
	}

	public void rectificate()
	{
		setControlPoints(new Point3d[] {});
	}

	public void setControlPoints(Point3d[] controlPoints)
	{
		if (Arrays.equals(this.controlPoints, controlPoints))
			return;

		this.controlPoints = controlPoints;
		GraphEngine.getInstance().getUIGraphListeners().doChange(this);
	}

	public void moveToCanvasPosition(TransformGroup transformGroup, int x, int y)
	{
		Point2d hitPoint = new Point2d(x, y);
		Object[] res = UIUtilities.getDraggedControlPointIndex(transformGroup, coordinates, controlPoints, hitPoint);
		int controlPointDraggedIndex = (Integer) res[0];
		controlPoints = (Point3d[]) res[1];

		Point3d draggedControlPoint = controlPoints[controlPointDraggedIndex];
		controlPoints[controlPointDraggedIndex] = GeometryUtil.canvasToVWorldPosition(draggedControlPoint, hitPoint, transformGroup);

		GraphEngine.getInstance().getUIGraphListeners().doChange(this);
	}

	public Geometry buildGeometry()
	{
		coordinates = buildCoordinates();

		if (StyleFacade.isShowControlPoints())
		{
			//remove old ones
			List<Node> childs = new ArrayList();
			for (int i = 0; i < branchGroup.numChildren(); i++)
			{
				Node child = branchGroup.getChild(i);
				if (child.equals(shape))
					continue;

				childs.add(child);
			}
			for (Node child : childs)
				branchGroup.removeChild(child);

			//add new ones
			for (int i = 0; i < controlPoints.length; i++)
			{
				Sphere sphere = new Sphere(0.01f);
				TransformGroup tg = new TransformGroup();
				tg.addChild(sphere);
				Transform3D transform = new Transform3D();
				transform.setTranslation(new Vector3d(controlPoints[i]));
				tg.setTransform(transform);

				BranchGroup subBranchGroup = new BranchGroup();
				subBranchGroup.setCapability(BranchGroup.ALLOW_DETACH);
				subBranchGroup.addChild(tg);
				branchGroup.addChild(subBranchGroup);
			}
		}

		LineStripArray lineStripArray = new LineStripArray(coordinates.length, GeometryArray.COORDINATES, new int[] { coordinates.length });
		lineStripArray.setCoordinates(0, coordinates);
		shape.setGeometry(lineStripArray);
		return lineStripArray;
	}

	private Point3d[] buildCoordinates()
	{
		if (controlPoints.length == 0)
		{
			return new Point3d[] { v1.getPosition(), v2.getPosition() };
		}
		else
		{
			Point3d[] result = new Point3d[2 + (controlPoints.length + 1) * SplineUtilities.SEGMENT_SIZE];

			Point3d[] points = new Point3d[2 + controlPoints.length];
			points[0] = v1.getPosition();
			for (int i = 1; i <= controlPoints.length; i++)
				points[i] = new Point3d(controlPoints[i - 1]);
			points[controlPoints.length + 1] = v2.getPosition();

			Point3d[] segment = SplineUtilities.draw(points);
			for (int j = 0; j < segment.length; j++)
				result[j + 1] = segment[j];
			result[0] = v1.getPosition();
			result[segment.length + 1] = v2.getPosition();
			return result;
		}
	}

	public double getWidth()
	{
		throw new UnsupportedOperationException();
	}

	public void setWidth(double width)
	{
		throw new UnsupportedOperationException();
	}

}
